﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace DecisionTree
{
    /// <summary>
    ///   Decision Tree (DT) Node.
    /// </summary>
    /// 
    /// <remarks>
    ///   Each node of a decision tree can play two roles. When a node is not a leaf, it
    ///   contains a <see cref="DecisionBranchNodeCollection"/> with a collection of child nodes. The
    ///   branch specifies an attribute index, indicating which column from the data set
    ///   (the attribute) should be compared against its children values. The type of the
    ///   comparison is specified by each of the children. When a node is a leaf, it will
    ///   contain the output value which should be decided for when the node is reached.
    /// </remarks>
    /// 
    /// <seealso cref="DecisionTree"/>
    /// 
    [Serializable]
    public class DecisionNode
    {
        public Dictionary<int, byte> RightAttributeValues;
        public List<DecisionNode> Branches;

        /// <summary>
        /// All inputs comes to this node.
        /// </summary>
        public int[] NodeInputOutputIDs { get; set; }
        
        /// <summary>
        ///   Creates a new decision node.
        /// </summary>
        /// 
        /// <param name="owner">The owner tree for this node.</param>
        /// 
        public DecisionNode(DecisionTree owner)
        {
            Owner = owner;
            Branches = new List<DecisionNode>();
        }

        /// <summary>
        /// Decision tree level
        /// </summary>
        public int Level { get; set; }

        /// <summary>
        ///   If this is a leaf node, gets or sets the output
        ///   value to be decided when this node is reached.
        /// </summary>
        ///
        public int? Output { get; set; }

        public double[] NodeHistogram;

        /// <summary>
        ///   Gets or sets the parent of this node. If this is a root
        ///   node, the parent is <c>null</c>.
        /// </summary>
        /// 
        public DecisionNode Parent { get; set; }

        /// <summary>
        ///   Gets the <see cref="DecisionTree"/> containing this node.
        /// </summary>
        /// 
        public DecisionTree Owner { get; set; }
        
        /// <summary>
        ///   Gets a value indicating whether this instance is a root node (has no parent).
        /// </summary>
        /// 
        /// <value><c>true</c> if this instance is a root; otherwise, <c>false</c>.</value>
        /// 
        public bool IsRoot
        {
            get { return Parent == null; }
        }

        /// <summary>
        ///   Gets a value indicating whether this instance is a leaf (has no children).
        /// </summary>
        /// 
        /// <value><c>true</c> if this instance is a leaf; otherwise, <c>false</c>.</value>
        /// 
        public bool IsLeaf
        {
            get { return Branches == null || Branches.Count == 0; }
        }

        /// <summary>
        ///   Computes whether a value satisfies
        ///   the condition imposed by this node.
        /// </summary>
        /// 
        /// <param name="x">The value x.</param>
        /// 
        /// <returns><c>true</c> if the value satisfies this node's
        /// condition; otherwise, <c>false</c>.</returns>
        /// 
        public bool Compute(byte[] input)
        {
            for (int i = 0; i < RightAttributeValues.Count; i++)
            {
                int idx = RightAttributeValues.ElementAt(i).Key;
                byte value = RightAttributeValues.ElementAt(i).Value;

                if (input[idx] != value)
                    return false;               
            }

            return true;
        }

        /// <summary>
        ///   Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// 
        /// <returns>
        ///   A <see cref="System.String"/> that represents this instance.
        /// </returns>
        /// 
        public override string ToString()
        {
            if (IsRoot)
                return "Root";

            string rVal = string.Empty;

            for (int i = 0; i < RightAttributeValues.Count; i++)
            {
                int idx = RightAttributeValues.Keys.ElementAt(i);
                byte value = RightAttributeValues.Values.ElementAt(i);

                rVal += idx.ToString() + " == " + value.ToString() + ",";
            }

            return rVal;
        }
    }
}
